//
// Created by Max on 2024/4/23.
//
#include "TvThread.h"

TvThread::TvThread(const TvMWThreadFunc func, const void *arg, ITvThreadEvt *pEvt)
        : mThreadFunc(func), mArg(arg), mEvt(pEvt), mId(0) {
    mStat.wait = false;
    mStat.once = false;
    mStat.exit = true;
    int s32Ret = pthread_mutex_init(&mLock, nullptr);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);

    s32Ret = pthread_cond_init(&mCond, nullptr);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);
}

TvThread::~TvThread() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        s32Ret = pthread_mutex_unlock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);

        s32Ret = pthread_join(mId, nullptr);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);
    } else {
        s32Ret = pthread_mutex_unlock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);
    }

    s32Ret = pthread_mutex_destroy(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);

    s32Ret = pthread_cond_destroy(&mCond);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_init fail, ret:0x%x", s32Ret);
}

void *TvThread::Loop(void *arg) {
    TvThread *ptr = reinterpret_cast<TvThread *>(arg);

    while (!ptr->mStat.exit) {
        ptr->Task();
    }

    if (ptr->mEvt != nullptr) {
        ptr->mEvt->OnTvMWThreadExit(ptr);
    }

    return nullptr;
}

void TvThread::Task() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    while (mStat.wait) {
        s32Ret = pthread_cond_wait(&mCond, &mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_wait fail, ret:0x%x", s32Ret);
    }

    if (mStat.exit) {
        s32Ret = pthread_mutex_unlock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
        return;
    }

    mStat.wait = mStat.once;
    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);

    if (mThreadFunc == nullptr) {
        return Run(mArg);
    }

    return mThreadFunc(mArg);
}

void TvThread::Exit() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        mStat.exit = true;
        mStat.wait = false;
        s32Ret = pthread_cond_signal(&mCond);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_signal fail, ret:0x%x", s32Ret);
    }
    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
}

void TvThread::ExitAndJoin() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        mStat.exit = true;
        mStat.wait = false;

        s32Ret = pthread_cond_signal(&mCond);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_signal fail, ret:0x%x", s32Ret);

        s32Ret = pthread_mutex_unlock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);

        s32Ret = pthread_join(mId, nullptr);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_join fail, ret:0x%x", s32Ret);
        return;
    }

    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
}

void TvThread::Run(unsigned int flag) {
    unsigned int flagWait = (~flag) & WAIT;
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        mStat.exit = true;
        mStat.wait = false;

        s32Ret = pthread_cond_signal(&mCond);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_signal fail, ret:0x%x", s32Ret);

        s32Ret = pthread_mutex_unlock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);

        s32Ret = pthread_join(mId, nullptr);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_join fail, ret:0x%x", s32Ret);

        s32Ret = pthread_mutex_lock(&mLock);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);
    }

    mStat.exit = false;
    mStat.wait = true;
    mStat.once = flag & ONCE;

    if (flagWait != 0) {
        mStat.wait = false;
    }
    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);

    s32Ret = pthread_create(&mId, nullptr, Loop, this);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_create fail, ret:0x%x", s32Ret);
}

void TvThread::Suspend() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        if (mStat.wait) {
            s32Ret = pthread_mutex_unlock(&mLock);
            RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
            return;
        }

        mStat.wait = true;
    }
    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
}

void TvThread::Resume() {
    int s32Ret = pthread_mutex_lock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_lock fail, ret:0x%x", s32Ret);

    if (!mStat.exit) {
        if (!mStat.wait) {
            s32Ret = pthread_mutex_unlock(&mLock);
            RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
            return;
        }

        mStat.wait = false;
        s32Ret = pthread_cond_signal(&mCond);
        RET_CHECK_ERR_LOG(s32Ret, "pthread_cond_signal fail, ret:0x%x", s32Ret);
    }
    s32Ret = pthread_mutex_unlock(&mLock);
    RET_CHECK_ERR_LOG(s32Ret, "pthread_mutex_unlock fail, ret:0x%x", s32Ret);
}
